package db.nio;

import db.nio.worker.BackWorker;
import db.nio.worker.FrontWorker;
import db.nio.worker.Worker;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异步服务器端
 * Created by daibin on 16-9-15.
 */
public class NioServer<T> extends NioBase<T> {

    /**
     * 当前监听线程
     */
    private ServerSocketChannel SERVER_SOCKET_CHANNEL;

    /**
     * 地址
     */
    private InetSocketAddress address;

    /**
     * boss线程
     */
    private Worker frontWorker;

    /**
     * 员工线程
     */
    private volatile List<Worker> backWorkerList;

    /**
     * 构造函数
     *
     * @param poolExecutor 线程池
     * @param framerClass  协议封装器
     * @param encoder      编码器
     * @param decoder      解码器
     * @param processor    处理器
     */
    public NioServer(ThreadPoolExecutor poolExecutor, Class<? extends Framer> framerClass, Encoder<T> encoder, Decoder<T> decoder, Processor<T> processor) {
        super(poolExecutor, framerClass, encoder, decoder, processor);
    }


    public NioServer bind(InetSocketAddress address) {
        this.address = address;
        return this;
    }


    public void startup() throws Exception {

        //后台员工
        int size = Runtime.getRuntime().availableProcessors();
        backWorkerList = new ArrayList<Worker>(size);
        for (int i = 0; i < size; i++)
            backWorkerList.add(new BackWorker(poolExecutor));

        //前台员工
        frontWorker = new FrontWorker(poolExecutor, backWorkerList, processor, encoder, decoder, framerClass);
        SERVER_SOCKET_CHANNEL = ServerSocketChannel.open();
        SERVER_SOCKET_CHANNEL.socket().bind(address);
        SERVER_SOCKET_CHANNEL.configureBlocking(false);
        SERVER_SOCKET_CHANNEL.register(frontWorker.selector, SelectionKey.OP_ACCEPT);
        for (Worker worker : backWorkerList) {
            worker.start();
        }
        frontWorker.start();
    }


    public void shutdown() throws IOException {
        frontWorker.stop = true;
        frontWorker.selector.wakeup();

        for (Worker worker : backWorkerList) {
            worker.stop = true;
            worker.selector.wakeup();
        }

        poolExecutor.shutdown();
        SERVER_SOCKET_CHANNEL.close();
    }

}
